package mikejyg.javaipacman.pacman.TSP;

import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

import mikejyg.javaipacman.pacman.cmaze;
import mikejyg.javaipacman.pacman.cpac;
import mikejyg.javaipacman.pacman.cpcman;
import mikejyg.javaipacman.pacman.cpowerdot;
import mikejyg.javaipacman.pacman.ctables;

/**
 * controls the pacmandude remotely
 * 
 * @author dnaumer
 * @version 0.4a
 * 
 */
public class travelingPacmanProblem extends cpcman {

	// initialize String array with size of cpowerdot.iX.lenght
	protected static String[] powerdotPositions = new String[cpowerdot.iX.length];
	protected static String lastPosition = "";
	protected static int lastDirection = 0;
	
	
	private static int jitterCounter = 0;
	private static int wallcounter = 0;
	private static int old_dir =0;
	private static boolean changedir = false;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

	public static void startTPP() {

		getPowerDotPositions();

		// New thread is generated. This Thread handles the algorithm for TSP in
		// background so Pacdude can move properly in one Direction until the
		// TSP algorithm return another direction.
		new Thread(new Runnable() {
			@Override
			public void run() {
				// get the powerdot positions
				while (true) {

					// get pacman position
					String pacmanPosition = getPacmanPosition();

					// find the nearest powerdot
					final int nextPowerdot = findNextPowerdot(pacmanPosition);

					// delete the point if reached
					if (pacmanPosition.equals(powerdotPositions[nextPowerdot])) {
						// how to delete??
						deleteReachedPowerdotFromArray(powerdotPositions[nextPowerdot]);
						// System.exit(-1);
					} else {
						movePacmanToPosition(powerdotPositions[nextPowerdot]);
					}
					
				}
			}
		}).start();

	}

	/**
	 * get the powerdot positions in an array
	 * 
	 */
	private static void getPowerDotPositions() {

		// information in array PD in cpac.java
		int[] xpositions = cpowerdot.iX;
		int[] ypositions = cpowerdot.iY;

		for (int count = 0; count < xpositions.length; count++) {

			powerdotPositions[count] = xpositions[count] + ","
					+ ypositions[count];

		}

	}

	/**
	 * Uses the newest pacmanposition and the position of the powerdots, to
	 * calculate the nearest powerdot to pacman
	 * 
	 * TODO: delete dots, that were reached and eaten
	 * 
	 * @param powerDotPositions
	 * @param pacmanPosition
	 * @return
	 */
	private static int findNextPowerdot(String pacmanPosition) {

		// lenght must be maximum
		double lenghtOfVector = Math.sqrt(Math.pow(cmaze.HEIGHT, 2.0)
				+ Math.pow(cmaze.WIDTH, 2.0));
		double distance = 0.0;

		int pickedPowerDot = 0;

		// use both points and walk trought them with for
		for (int counter = 0; counter < powerdotPositions.length; counter++) {

			// get the string
			String powerDotPosition = powerdotPositions[counter];

			if (powerdotPositions[counter] == null) {
				distance = lenghtOfVector;
			} else {
				distance = getDistanceBetweenVectors(pacmanPosition,
						powerDotPosition);
			}

			// set lenghtOfVector new if smaller
			if (distance < lenghtOfVector && powerdotPositions[counter] != null) {

				// set it
				lenghtOfVector = distance;

				// pick the new powerDotPosition
				pickedPowerDot = counter;

			}

		}

		return pickedPowerDot;

	}

	/**
	 * Gets the current position of pacman from cpac.java
	 * 
	 * @return
	 */
	private static String getPacmanPosition() {

		// get the position from cpac and divide by 16
		int pacmanXPos = cpac.iX / 16;
		int pacmanYPos = cpac.iY / 16;

		// create a returnstring
		String returnString = pacmanXPos + "," + pacmanYPos;

		// return that motherfucker
		return returnString;

	}

	/**
	 * Takes the current position of pacman and uses the direction to calculate
	 * the future positions
	 * 
	 * @param currentPacmanPosition
	 * @param pacmanDirection
	 *            (-1 for optional)
	 * @return
	 */
	protected static String getFuturePacmanPosition(
			String currentPacmanPosition, int pacmanDirection) {

		// initilizing future positions
		int futureXPos = 0;
		int futureYPos = 0;

		// get the current direction from cpcman if -1
		if (pacmanDirection == -1) {
			pacmanDirection = cpcman.pacKeyDir;
		}

		// get x and y of pacmans current position
		String[] currentPacmanPositions = currentPacmanPosition.split(",");
		int currentXPos = Integer.parseInt(currentPacmanPositions[0]);
		int currentYPos = Integer.parseInt(currentPacmanPositions[1]);

		// future position is the extended current position -> set
		// currentposition to future position
		futureXPos = currentXPos;
		futureYPos = currentYPos;

		// use the direction just as in tables
		switch (pacmanDirection) {
		case 0:
			futureXPos = currentXPos + 1;
			break;
		case 1:
			futureYPos = currentYPos - 1;
			break;
		case 2:
			futureXPos = currentXPos - 1;
			break;
		case 3:
			futureYPos = currentYPos + 1;
			break;
		default:
			System.err.println("Error on getPacmanFuturePosition");

		}

		// generate return string
		String returnString = futureXPos + "," + futureYPos;

		return returnString;

	}

	/**
	 * Needs pacmans current position and the position of the dot and returns
	 * the distance via vector calculation
	 * 
	 * @return
	 */
	private static double getDistanceBetweenVectors(String vectorA,
			String vectorB) {

		// split into x and y
		String[] vectorAArray = vectorA.split(",");
		String[] vectorBArray = vectorB.split(",");

		// split the positions in four variables
		int vectorAX = Integer.parseInt(vectorAArray[0]);
		int vectorAY = Integer.parseInt(vectorAArray[1]);
		int vectorBX = Integer.parseInt(vectorBArray[0]);
		int vectorBY = Integer.parseInt(vectorBArray[1]);

		// calc the length
		int xValue = vectorAX - vectorBX;
		int yValue = vectorAY - vectorBY;

		double distance = Math.sqrt(Math.pow(xValue, 2.0)
				+ Math.pow(yValue, 2.0));

		return distance;

	}

	/**
	 * @deprecated FUNCTION IS DEPRECATED
	 * 
	 *             Looks for the next direction Checks if this direction is
	 *             possible, using checkIfPositionIsWalkable Also uses
	 *             getDistanceBetweenVectors to check if the way is really
	 *             intelligent or not
	 * 
	 * @param destination
	 * @param currentPosition
	 * @return
	 */
	private static int findNextDirection(String destination,
			String currentPosition) {

		// get x and y from destination
		String destinations[] = destination.split(",");
		int destinationX = Integer.parseInt(destinations[0]);
		int destinationY = Integer.parseInt(destinations[1]);

		// get x and y from currentPosition
		String positions[] = currentPosition.split(",");
		int posX = Integer.parseInt(positions[0]);
		int posY = Integer.parseInt(positions[1]);

		// which coordinate is further away
		int differenceX = destinationX - posX;
		int differenceY = destinationY - posY;

		int newDirection = -1;

		if (Math.abs(differenceX) >= Math.abs(differenceY)) {

			if (differenceX < 0
					&& checkIfPositionIsWalkable(getFuturePacmanPosition(
							currentPosition, 2))) {

				newDirection = 2;

			} else if (checkIfPositionIsWalkable(getFuturePacmanPosition(
					currentPosition, 0))) {

				newDirection = 0;

			}
		} else {

			if (differenceY < 0
					&& checkIfPositionIsWalkable(getFuturePacmanPosition(
							currentPosition, 1))) {

				newDirection = 1;

			} else if (checkIfPositionIsWalkable(getFuturePacmanPosition(
					currentPosition, 3))) {

				newDirection = 3;

			}
		}

		// if everything fails and directionString is still null
		// if (newDirection == -1){

		// use the more fancy calculation as a fallback
		newDirection = checkForBestDirection(destination, currentPosition);

		// }

		return newDirection;

	}

	/**
	 * Moves pacman to given position
	 * 
	 * @param x
	 * @param y
	 */
	protected static void movePacmanToPosition(String givenPosition) {

		// use pacKeyDir to move pacman
		if ((old_dir == checkForBestDirection(givenPosition,
				getPacmanPosition()) && (checkIfPositionIsWalkable(getFuturePacmanPosition(
				getPacmanPosition(), 0)))) == false) {
			wallcounter++;

			if (wallcounter < 200000) {
				cpcman.pacKeyDir = checkForBestDirection(givenPosition,
						getPacmanPosition());

			} else {
				cpcman.pacKeyDir = checkForBestDirection("10,10",
						getPacmanPosition());

				if (wallcounter > 220000) {
					// System.out.println("HIER");
					wallcounter = 0;

				}

			}
		}

		old_dir = cpcman.pacKeyDir;
	}


	/**
	 * Checks for the best of the possible move directions
	 * 
	 * @param destination
	 * @param currentPosition
	 * @return
	 */
	private static int checkForBestDirection(String destination,
			String currentPosition) {

		
		Integer[] possibleDirections = returnPossibleMovements(currentPosition);

		// maximum distance
		double distance = Math.sqrt(Math.pow(cmaze.HEIGHT, 2.0)
				+ Math.pow(cmaze.WIDTH, 2.0));
		double tempDistance = 0.0;

		// we need that thingy
		int returnDirection = 0;
		
		
		for (int counter = 0; counter < possibleDirections.length; counter++) {

			// calculate the distance for later purposes
			tempDistance = getDistanceBetweenVectors(
					getFuturePacmanPosition(currentPosition,
							possibleDirections[counter]), destination);

			if (distance > tempDistance) {

				// set new nearest distance
				distance = tempDistance;

				// and write back the possible and thereby best direction
				returnDirection = possibleDirections[counter];

			}

		}
		
		
		//part where we detect the jitter
		if (lastPosition.equals(getFuturePacmanPosition(currentPosition, returnDirection))){

			jitterCounter++;
			
			if (jitterCounter > 5){
				
				
				//IT HAPPENS, ooooh crap....
				//dunno what to do
				//perhaps a unicorn helps
//				\
//				 \ji
//				 /.((( 
//				(,/"(((__,--.
//				    \  ) _( /{ 
//				    !|| " :||      
//				    !||   :|| 
//				 '''   ''' 
				//damn...	
				switch(returnDirection) {
				case ctables.DOWN:
					returnDirection = ctables.UP;
					break;
					
				case ctables.UP:
					//if(checkIfPositionIsWalkable(getFuturePacmanPosition(currentPosition, 0)))
						returnDirection = ctables.DOWN;
					
					break;
					
				case ctables.LEFT:
					//if(checkIfPositionIsWalkable(getFuturePacmanPosition(currentPosition, 0)))
						returnDirection = ctables.RIGHT;
					break;
					
				case ctables.RIGHT:
					returnDirection= ctables.LEFT;
					break;
				}

				return returnDirection;
				
				
			}
			
			
		}

		//set new last direction and new last position
		lastPosition = currentPosition;
		return returnDirection;
		
		
	}

	/**
	 * Delete the reached powerdot from the positions array
	 * 
	 * @param positionOfPowerdot
	 */
	private static void deleteReachedPowerdotFromArray(String positionOfPowerdot) {

		// get x and y from destination
		String destinations[] = positionOfPowerdot.split(",");
		int destinationX = Integer.parseInt(destinations[0]);
		int destinationY = Integer.parseInt(destinations[1]);

		for (int counter = 0; counter < powerdotPositions.length; counter++) {

			if (powerdotPositions[counter] == positionOfPowerdot) {

				powerdotPositions[counter] = null;

			}

		}

	}

	/**
	 * Returns an Integer Array of possible movement directions
	 * 
	 * @param currentPosition
	 * @return
	 */
	private static Integer[] returnPossibleMovements(String currentPosition) {

		// initilize array
		List<Integer> directionsArray = new ArrayList<Integer>();

		// check all 4 posibilities
		for (Integer counter = 0; counter <= 3; counter++) {

			// only add the possible movements
			if (checkIfPositionIsWalkable(getFuturePacmanPosition(
					currentPosition, counter))) {

				directionsArray.add(counter);

			}

		}

		// transform the List into an Integer[] array
		Integer[] returnArray = new Integer[directionsArray.size()];
		returnArray = directionsArray.toArray(returnArray);
		return returnArray;

	}

	/**
	 * Returns the iMaze information on the position given
	 * 
	 * @param position
	 * @return
	 */
	protected static boolean checkIfPositionIsWalkable(String position) {

		// get maze
		int tempIMaze[][] = cmaze.iMaze;

		// get positions
		String[] positions = position.split(",");
		int posX = Integer.parseInt(positions[0]);
		int posY = Integer.parseInt(positions[1]);

		// write down the value of the position
		int valueOfPosition = tempIMaze[posY][posX];

		switch (valueOfPosition) {
		case 0:
			return true;
		case 1:
			return false;
		case 2:
			return false;
		case 4:
			return true;
		case 8:
			return true;
		default:
			System.err.println("Error in checkIfPositionIsWalkable");
			return false;

		}

	}

	/**
	 * Give Pacman the direction
	 * 
	 * @param direction
	 * 
	 */
	public static void movePacman(int direction) {
		pacKeyDir = direction;
	}

}
